﻿using System;
using System.Linq;
using System.Windows.Data;
using System.ComponentModel;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Collections;
using System.ServiceModel.DomainServices.Client;
using System.Reflection;
using System.Globalization;
using System.Collections.ObjectModel;

namespace SL.MVVMS.RIA
{
    public class RIACollectionView<T> : ICollection<T>, INotifyCollectionChanged,
        INotifyPropertyChanged, ICollectionView, IEditableCollectionView
        where T : Entity, new()
    {
        DomainContext domainContext;
        EntitySet<T> entitySet;

        public bool NeedsCommit
        {
            get
            {
                return (entitySet.HasChanges ||
                    IsAddingNew ||
                    IsEditingItem);
            }
        }

        public bool CanCommit
        {
            get
            {
                return (entitySet.HasChanges &&
                    IsAddingNew == false &&
                    IsEditingItem == false);
            }
        }

        public RIACollectionView(DomainContext context)
        {
            this.domainContext = context;
            if (context == null)
            {
                this.entitySet = new EntitySet<T>();
            }
            else
            {
                this.entitySet = context.EntityContainer.GetEntitySet<T>();
            }
            ((INotifyCollectionChanged)this.entitySet).CollectionChanged += new NotifyCollectionChangedEventHandler(EntitySet_CollectionChanged);
            this.sortDescriptions = new SortDescriptionCollection();
            ((INotifyCollectionChanged)this.sortDescriptions).CollectionChanged += new NotifyCollectionChangedEventHandler(SortDescriptions_CollectionChanged);
            this.GroupDescriptions = new System.Collections.ObjectModel.ObservableCollection<GroupDescription>();
            this.GroupDescriptions.CollectionChanged += new NotifyCollectionChangedEventHandler(GroupDescriptions_CollectionChanged);

            orderedList = new List<T>();
            isReady = false;
            deferCount = 0;
        }

        /// <summary>
        /// This Event Handler Monitors the Underlying EntitySet and handles Changes into the View
        /// as Needed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void EntitySet_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            // Do nothing if we are told to Defer Refreshing
            if (deferCount > 0)
                return;

            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                // TODO: Inject New Items
                foreach (T item in e.NewItems.Cast<T>())
                {
                    int idxWhereAdded = 0;

                    // Ignore Brand New Items in the Add State
                    // which are at the end of the list
                    if (item.EntityState == EntityState.New)
                    {
                        if (this.NewItemPlaceholderPosition == System.ComponentModel.NewItemPlaceholderPosition.AtEnd)
                        {
                            orderedList.Add(item);
                            idxWhereAdded = orderedList.Count - 1;
                        }
                    }

                    // Check for Filtering
                    if (Filter != null &&
                        Filter(item) == false)
                        return;
                    // Figure out what sort position this should be at
                    if (SortDescriptions == null ||
                        SortDescriptions.Count == 0)
                        orderedList.Add(item);
                    else
                    {
                        foreach (var sortDesc in SortDescriptions)
                        {
                            object newvalue = getProp(sortDesc.PropertyName).GetValue(item, null);
                            while (idxWhereAdded < orderedList.Count)
                            {
                                object listvalue = getProp(sortDesc.PropertyName).GetValue(orderedList[idxWhereAdded], null);
                                int compareResult = ((IComparable)listvalue).CompareTo(newvalue);
                                if (sortDesc.Direction == ListSortDirection.Ascending)
                                {
                                    if (compareResult < 0)
                                        idxWhereAdded++;
                                    else
                                        break;
                                }
                                else
                                {
                                    if (compareResult > 0)
                                        idxWhereAdded++;
                                    else
                                        break;
                                }
                            }
                        }
                        orderedList.Insert(idxWhereAdded, item);
                    }

                    // Apply to any groups as needed

                    // Notify Consumers of this Change
                    RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(
                        NotifyCollectionChangedAction.Add,
                        item, idxWhereAdded));
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                // TODO: Remove Old Items
                foreach (T item in e.OldItems.Cast<T>())
                {
                    if (orderedList.Contains(item))
                    {
                        int idxRemoved = orderedList.IndexOf(item);
                        orderedList.Remove(item);
                        // Remove from Groups as Needed

                        // Notify Consumers of this Change
                        RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(
                            NotifyCollectionChangedAction.Remove,
                            item, idxRemoved));
                    }
                }
            }
            else
            {
                FilterSortAndGroup();
                RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(
                    NotifyCollectionChangedAction.Reset));
            }
        }

        void SortDescriptions_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            // Just apply a Complete Re-sort at this point.
            List<T> originalOrder = orderedList;
            FilterSortAndGroup();
            RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(
                NotifyCollectionChangedAction.Reset));
        }

        public int Count
        {
            get
            {
                if (orderedList != null)
                    return orderedList.Count;
                else
                    return entitySet.Count;
            }
        }

        #region ICollection<T> Implementation
        public IEnumerator<T> GetEnumerator()
        {
            if (isReady == false)
                FilterSortAndGroup();

            if (orderedList.Count != entitySet.Count)
                return entitySet.GetEnumerator();

            return orderedList.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            if (isReady == false)
                FilterSortAndGroup();

            if (orderedList.Count != entitySet.Count)
                return entitySet.GetEnumerator();

            return orderedList.GetEnumerator();
        }

        public int IndexOf(T item)
        {
            return orderedList.IndexOf(item);
        }

        public void Insert(int index, T item)
        {
            orderedList.Insert(index, item);
            entitySet.Add(item);
            RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(
                NotifyCollectionChangedAction.Add, item, index));
            MoveCurrentTo(item);
        }

        public T this[int index]
        {
            get
            {
                return orderedList[index];
            }
            set
            {
                orderedList[index] = value;
            }
        }

        public void Add(T item)
        {
            orderedList.Add(item);
            entitySet.Add(item);
            RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(
                NotifyCollectionChangedAction.Add, item, orderedList.IndexOf(item)));
        }

        public void Clear()
        {
            orderedList.Clear();
            entitySet.Clear();
            RaiseCollectionChanged(
                new NotifyCollectionChangedEventArgs(
                    NotifyCollectionChangedAction.Reset));
        }

        public bool Contains(T item)
        {
            return orderedList.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            orderedList.CopyTo(array, arrayIndex);
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public bool Remove(T item)
        {
            int idxRemoved = orderedList.IndexOf(item);

            if (idxRemoved == -1)
                return false;

            orderedList.Remove(item);
            entitySet.Remove(item);

            RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(
                NotifyCollectionChangedAction.Remove,
                item, idxRemoved));

            return true;
        }
        #endregion

        #region INotifyCollectionChanged Implementation
        public event NotifyCollectionChangedEventHandler CollectionChanged;

        private bool isRaisingCollectionChanged = false;
        private void RaiseCollectionChanged(NotifyCollectionChangedEventArgs args)
        {
            if (CollectionChanged != null &&
                isRaisingCollectionChanged == false)
            {
                isRaisingCollectionChanged = true;
                CollectionChanged(this, args);
                isRaisingCollectionChanged = false;
            }
        }
        #endregion

        #region INotifyPropertyChanged Implementation
        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string PropertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
        }
        #endregion

        #region ICollectionView Implementation

        private SortDescriptionCollection sortDescriptions;
        private bool isReady;
        private List<T> orderedList;    // This holds our Ordered Results
        private int currentIndex;
        private ObservableCollection<object> internalGroups;


        #region Defer Refresh Implementation

        int deferCount;

        private class DeferHold : IDisposable
        {
            private RIACollectionView<T> _collection;

            public DeferHold(RIACollectionView<T> collection)
            {
                this._collection = collection;
            }

            public void Dispose()
            {
                this._collection.EndDeferral();
                this._collection = null;

                GC.SuppressFinalize(this);
            }
        }

        private void EndDeferral()
        {
            --deferCount;
            if (deferCount == 0)
                FilterSortAndGroup();
        }

        #endregion

        #region Sort, Filter, and Group  Routine

        Predicate<object> filter;

        /// <summary>
        /// Primary Entry Point for Applying Filtering, Sorting, and Grouping Logic
        /// Applies the Logic in this Order, Filter, then Sort, then Group
        /// </summary>
        void FilterSortAndGroup()
        {
            // Do nothing if we are told to Defer Refreshing
            if (deferCount > 0)
                return;

            // Keep a Reference to the Current Selection
            T currentSelection = CurrentItem as T;

            #region Filter as Necessary
            IEnumerable<T> filterQuery;
            if (filter != null)
                filterQuery = entitySet.Where((entity) => { return filter(entity); });
            else
                filterQuery = entitySet.AsEnumerable();
            #endregion

            if (sortDescriptions.Count > 0)
            {
                #region Sort Logic Implementation
                SortDescription sd = sortDescriptions[0];
                IOrderedEnumerable<T> orderSort;
                // Inject Groups a Sorting, then the first sort
                if (GroupDescriptions.Count > 0)
                {
                    // Add the first Grouping
                    orderSort = filterQuery.OrderBy<T, dynamic>(a =>
                    {
                        return GroupDescriptions[0].GroupNameFromItem(a, 0, Culture);
                    });

                    // Add other Groupings Provided
                    for (int g = 1; g < GroupDescriptions.Count; g++ )
                    {
                        orderSort = orderSort.ThenBy<T, dynamic>(a =>
                        {
                            return GroupDescriptions[g].GroupNameFromItem(a, g, Culture);
                        });
                    }
                    // Starting Sort begins with ThenBy When Doing Grouping
                    if (sd.Direction == ListSortDirection.Ascending)
                    {
                        orderSort = orderSort.ThenBy<T, dynamic>(a =>
                        {
                            return getProp(sd.PropertyName).GetValue(a, null);
                        });
                    }
                    else
                    {
                        orderSort = orderSort.ThenByDescending<T, dynamic>(a =>
                        {
                            return getProp(sd.PropertyName).GetValue(a, null);
                        });
                    }
                }
                else
                { 
                    // Starting Sort for Non-Grouping Item
                    if (sd.Direction == ListSortDirection.Ascending)
                    {
                        orderSort = filterQuery.OrderBy<T, dynamic>(a =>
                        {
                            return getProp(sd.PropertyName).GetValue(a, null);
                        });
                    }
                    else
                    {
                        orderSort = filterQuery.OrderByDescending<T, dynamic>(a =>
                        {
                            return getProp(sd.PropertyName).GetValue(a, null);
                        });
                    }
                }

                // Apply additional sort rules if provided
                for (int s = 1; s < sortDescriptions.Count; s++)
                {
                    sd = sortDescriptions[s];
                    if (sd.Direction == ListSortDirection.Ascending)
                    {
                        orderSort = orderSort.ThenBy<T, dynamic>(a =>
                        {
                            return getProp(sd.PropertyName).GetValue(a, null);
                        });
                    }
                    else
                    {
                        orderSort = orderSort.ThenByDescending<T, dynamic>(a =>
                        {
                            return getProp(sd.PropertyName).GetValue(a, null);
                        });
                    }
                }
                #endregion
                orderedList = orderSort.ToList();
            }
            else
            {
                orderedList = entitySet.ToList();
            }

            //createGroups();
            //addDataToGroups();
            addItemsToTopGroups();

            // Keep our current selection if Possible
            if (orderedList.Contains(currentSelection))
                MoveCurrentTo(currentSelection);
            isReady = true;
        }

        #region Group Logic

        #region old Logic (not working)
        ///// <summary>
        ///// Creates the Empty RIACollectionViewGroup Items to which data can be added
        ///// </summary>
        //void createGroups()
        //{
        //    if (internalGroups == null)
        //        internalGroups = new ObservableCollection<object>();
        //    else
        //        internalGroups.Clear();
        //    if (GroupDescriptions.Count > 0)
        //    {
        //        foreach (var item in orderedList)
        //        {
        //            // Get Group Names
        //            for (int l = 0; l < GroupDescriptions.Count; l++)
        //            {
        //                var group = GroupDescriptions[l];
        //                object name = group.GroupNameFromItem(item, l, CultureInfo.CurrentCulture);
        //                if (!group.GroupNames.Contains(name))
        //                    group.GroupNames.Add(name);
        //            }
        //        }
        //        // set the Root Groups
        //        foreach (var name in GroupDescriptions[0].GroupNames.OrderBy(g => g))
        //        {
        //            RIACollectionViewGroup newGroup = new RIACollectionViewGroup(null, name);
        //            internalGroups.Add(newGroup);
        //            addSubGroups(newGroup, 1);
        //        }
        //    }
        //}

        ///// <summary>
        ///// Recursive Function to Add Groups in a Nested Manner
        ///// </summary>
        ///// <param name="parentGroup"></param>
        ///// <param name="levelOfChildren"></param>
        //void addSubGroups(RIACollectionViewGroup parentGroup, int levelOfChildren)
        //{
        //    if (levelOfChildren >= GroupDescriptions.Count)
        //        return;
        //    foreach (var name in GroupDescriptions[levelOfChildren].GroupNames.OrderBy(g => g))
        //    {
        //        RIACollectionViewGroup newGroup = new RIACollectionViewGroup(parentGroup, name);
        //        parentGroup.Add(newGroup);
        //        addSubGroups(newGroup, levelOfChildren + 1);
        //    }
        //}

        ///// <summary>
        ///// Function to add all OrderedList Items to the Groups
        ///// </summary>
        //void addDataToGroups()
        //{
        //    foreach (var item in orderedList)
        //    {
        //        // Get Group Names
        //        addToGroups(item, Groups.Cast<RIACollectionViewGroup>());
        //    }
        //}

        ///// <summary>
        ///// Recursive Function to add an item to the Bottom Group it belongs in
        ///// </summary>
        ///// <param name="item"></param>
        ///// <param name="groups"></param>
        //void addToGroups(object item, IEnumerable<RIACollectionViewGroup> groups)
        //{
        //    RIACollectionViewGroup firstGroup = groups.First();
        //    int groupLevel = firstGroup.Level;
        //    GroupDescription levelDescription = GroupDescriptions[groupLevel];

        //    object itemName = levelDescription.GroupNameFromItem(item, groupLevel, CultureInfo.CurrentCulture);
        //    foreach (var group in groups)
        //    {
        //        if (levelDescription.NamesMatch(group.Name, itemName))
        //        {
        //            if (group.IsBottomLevel)
        //                group.Add(item);
        //            else
        //            {
        //                addToGroups(item, group.Items.Cast<RIACollectionViewGroup>());
        //            }
        //            break;
        //        }
        //    }
        //}
        #endregion


        void addItemsToTopGroups()
        {
            if (GroupDescriptions.Count == 0)
                return;

            if (internalGroups == null)
                internalGroups = new ObservableCollection<object>();
            else
                internalGroups.Clear();

            foreach (T item in orderedList)
            {
                addToTopGroup(item);
            }
            RaisePropertyChanged("Groups");
        }
        void addToTopGroup(T item)
        {
            var topGroupName = GroupDescriptions[0].GroupNameFromItem(item, 0, Culture);
            var containingGroup = internalGroups.Cast<RIACollectionViewGroup>().Where(
                g => GroupDescriptions[0].NamesMatch(g.Name, topGroupName)).FirstOrDefault();
            if (containingGroup == null)
            {
                GroupDescriptions[0].GroupNames.Add(topGroupName);
                containingGroup = new RIACollectionViewGroup(null, topGroupName);
                internalGroups.Add(containingGroup);
            }
            addToSubGroups(containingGroup, item, 1);
        }
        void addToSubGroups(RIACollectionViewGroup parent, T item, int descrIndex)
        {
            if (descrIndex >= GroupDescriptions.Count)
            {
                parent.Add(item);
                return;
            }

            var groupName = GroupDescriptions[descrIndex].GroupNameFromItem(item, descrIndex, Culture);
            var subGroup = parent.Items.Cast<RIACollectionViewGroup>().Where(
                g => GroupDescriptions[descrIndex].NamesMatch(g.Name, groupName)).FirstOrDefault();
            if (subGroup == null)
            {
                GroupDescriptions[descrIndex].GroupNames.Add(groupName);
                subGroup = new RIACollectionViewGroup(parent, groupName);
                parent.Add(subGroup);
            }
            addToSubGroups(subGroup, item, descrIndex+1);
        }

        #endregion


        #region Caching of Reflection Work (Property Info)
        Dictionary<string, PropertyInfo> piCache;
        PropertyInfo getProp(string propName)
        {
            if (piCache == null)
                piCache = new Dictionary<string, PropertyInfo>();
            if (!piCache.ContainsKey(propName))
                piCache.Add(propName, typeof(T).GetProperty(propName));

            return piCache[propName];
        }
        #endregion

        #endregion


        public bool CanFilter
        {
            get { return true; }
        }

        public bool CanGroup
        {
            get { return true; }
        }

        public bool CanSort
        {
            get { return true; }
        }

        public bool Contains(object item)
        {
            return entitySet.Contains(item as T);
        }

        public System.Globalization.CultureInfo Culture
        {
            get;
            set;
        }

        public event EventHandler CurrentChanged;

        bool isRaisingCurrentChanged = false;
        private void RaiseCurrentChanged()
        {
            if (CurrentChanged != null &&
                isRaisingCurrentChanged == false)
            {
                isRaisingCurrentChanged = true;
                CurrentChanged(this, EventArgs.Empty);
                isRaisingCurrentChanged = false;
            }
        }

        public event CurrentChangingEventHandler CurrentChanging;

        bool isRaisingCurrentChangingWithCancel = false;
        private bool RaiseCurrentChangingWithCancel()
        {
            if (CurrentChanging != null &&
                isRaisingCurrentChangingWithCancel == false)
            {
                isRaisingCurrentChangingWithCancel = true;
                CurrentChangingEventArgs args = new CurrentChangingEventArgs(true);
                CurrentChanging(this, args);
                isRaisingCurrentChangingWithCancel = false;
                return args.Cancel;
            }
            return false;
        }

        public object CurrentItem
        {
            get { return (orderedList.Count > currentIndex) ? orderedList[currentIndex] : null; }
        }

        public int CurrentPosition
        {
            get { return currentIndex; }
        }

        public IDisposable DeferRefresh()
        {
            return new DeferHold(this);
        }

        public Predicate<object> Filter
        {
            get
            {
                return filter;
            }
            set
            {
                filter = value;
                RaisePropertyChanged("Filter");
                FilterSortAndGroup();
            }
        }

        public System.Collections.ObjectModel.ObservableCollection<GroupDescription> GroupDescriptions
        {
            get;
            private set;
        }

        void GroupDescriptions_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            //createGroups();
            //addDataToGroups();
            addItemsToTopGroups();
            RaisePropertyChanged("Groups");
        }

        ReadOnlyObservableCollection<object> roGroups;
        public System.Collections.ObjectModel.ReadOnlyObservableCollection<object> Groups
        {
            get
            {
                if (roGroups == null)
                    roGroups = new ReadOnlyObservableCollection<object>(internalGroups);
                return roGroups;
            }
        }

        public bool IsCurrentAfterLast
        {
            get { return (currentIndex > entitySet.Count - 1); }
        }

        public bool IsCurrentBeforeFirst
        {
            get { return (currentIndex < 0); }
        }

        public bool IsEmpty
        {
            get { return (entitySet.Count == 0); }
        }

        public bool MoveCurrentTo(object item)
        {
            if (!RaiseCurrentChangingWithCancel())
            {
                int posOf = orderedList.IndexOf(item as T);
                if (posOf > -1)
                {
                    currentIndex = posOf;
                    RaiseCurrentChanged();
                    return true;
                }
            }
            return false;
        }

        public bool MoveCurrentToFirst()
        {
            if (!RaiseCurrentChangingWithCancel())
            {
                currentIndex = 0;
                RaiseCurrentChanged();
                if (entitySet.Count > 0)
                    return true;
            }
            return false;
        }

        public bool MoveCurrentToLast()
        {
            if (!RaiseCurrentChangingWithCancel())
            {
                if (currentIndex >= 0)
                {
                    currentIndex = entitySet.Count - 1;
                    RaiseCurrentChanged();
                    return true;
                }
            }
            return false;
        }

        public bool MoveCurrentToNext()
        {
            if (!RaiseCurrentChangingWithCancel())
            {
                if (currentIndex < entitySet.Count)
                {
                    currentIndex++;
                    RaiseCurrentChanged();
                    return true;
                }
            }
            return false;
        }

        public bool MoveCurrentToPosition(int position)
        {
            if (!RaiseCurrentChangingWithCancel())
            {
                if (0 <= position && position < entitySet.Count)
                {
                    currentIndex = position;
                    RaiseCurrentChanged();
                    return true;
                }
            }
            return false;
        }

        public bool MoveCurrentToPrevious()
        {
            if (!RaiseCurrentChangingWithCancel())
            {
                if (currentIndex > 0)
                {
                    currentIndex--;
                    RaiseCurrentChanged();
                    return true;
                }
            }
            return false;
        }

        public void Refresh()
        {
            FilterSortAndGroup();
        }

        public SortDescriptionCollection SortDescriptions
        {
            get { return sortDescriptions; }
        }

        public IEnumerable SourceCollection
        {
            get { return entitySet; }
        }
        #endregion

        #region IEditableCollectionView Implementation

        T currentNewItem;
        T currentEditItem;

        public object AddNew()
        {
            // this begins the add new function, but does not add it to the underlying source yet.
            currentNewItem = new T();
            entitySet.Add(currentNewItem);
            RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(
                NotifyCollectionChangedAction.Add,
                currentNewItem, orderedList.IndexOf(currentNewItem)));
            MoveCurrentTo(currentNewItem);
            RaisePropertyChanged("CurrentAddItem");
            RaisePropertyChanged("IsAddingItem");
            return currentNewItem;
        }

        public bool CanAddNew
        {
            get
            {
                return (entitySet.CanAdd);
            }
        }

        public bool CanCancelEdit
        {
            get { return IsEditingItem; }
        }

        public bool CanRemove
        {
            get { return entitySet.CanRemove; }
        }

        public void CancelEdit()
        {
            ((IEditableObject)currentEditItem).CancelEdit();
            currentEditItem = null;
            RaisePropertyChanged("CurrentEditItem");
            RaisePropertyChanged("IsEditingItem");
        }

        public void CancelNew()
        {
            ((IEditableObject)currentNewItem).CancelEdit();
            entitySet.Remove(currentNewItem);
            currentNewItem = null;
            MoveCurrentToPrevious();
            FilterSortAndGroup();
            RaisePropertyChanged("CurrentAddItem");
            RaisePropertyChanged("IsAddingItem");
        }

        public void CommitEdit()
        {
            T itemFinished = currentEditItem;
            ((IEditableObject)currentEditItem).EndEdit();
            currentEditItem = null;
            RaisePropertyChanged("CurrentEditItem");
            RaisePropertyChanged("IsEditingItem");
            reSortThisItem(itemFinished);
            MoveCurrentTo(itemFinished);
        }

        public void CommitNew()
        {
            T itemFinished = currentNewItem;
            ((IEditableObject)currentNewItem).EndEdit();
            currentNewItem = null;
            RaisePropertyChanged("CurrentAddItem");
            RaisePropertyChanged("IsAddingItem");
            reSortThisItem(itemFinished);
            MoveCurrentTo(itemFinished);
        }

        public object CurrentAddItem
        {
            get { return currentNewItem; }
        }

        public object CurrentEditItem
        {
            get { return currentEditItem; }
        }

        public void EditItem(object item)
        {
            currentEditItem = item as T;
            ((IEditableObject)currentEditItem).BeginEdit();
            RaisePropertyChanged("CurrentEditItem");
            RaisePropertyChanged("IsEditingItem");
        }

        void reSortThisItem(T item)
        {
            int oldIndex = orderedList.IndexOf(item);
            int newIndex = 0;
            if (oldIndex == newIndex)
                newIndex++;
            foreach (var sortDesc in SortDescriptions)
            {
                object newvalue = getProp(sortDesc.PropertyName).GetValue(item, null);
                while (newIndex < orderedList.Count)
                {
                    if (oldIndex == newIndex)
                        newIndex++;

                    object listvalue = getProp(sortDesc.PropertyName).GetValue(orderedList[newIndex], null);
                    int compareResult = ((IComparable)listvalue).CompareTo(newvalue);
                    if (sortDesc.Direction == ListSortDirection.Ascending)
                    {
                        if (compareResult < 0)
                            newIndex++;
                        else
                            break;
                    }
                    else
                    {
                        if (compareResult > 0)
                            newIndex++;
                        else
                            break;
                    }
                }
            }
            newIndex--;

            if (newIndex == oldIndex)
                return;     // The Sort does not change the position


            orderedList.RemoveAt(oldIndex);
            RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(
                NotifyCollectionChangedAction.Remove, item, oldIndex));
            orderedList.Insert(newIndex, item);
            RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(
                NotifyCollectionChangedAction.Add, item, newIndex));
        }

        public bool IsAddingNew
        {
            get { return (currentNewItem != null); }
        }

        public bool IsEditingItem
        {
            get { return (currentEditItem != null); }
        }

        public NewItemPlaceholderPosition NewItemPlaceholderPosition
        {
            get;
            set;
        }

        public void Remove(object item)
        {
            T toRemove = item as T;
            int idxRemoved = orderedList.IndexOf(toRemove);

            entitySet.Remove(toRemove); // Event Handler will handle Removing from the view

        }

        public void RemoveAt(int index)
        {
            T toRemove = orderedList[index];
            
            entitySet.Remove(toRemove); // Event Handler will handle Removing from the view

        }
        #endregion

    }
}
